{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 贝塞尔曲线简单实现"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "from celluloid import Camera  # 保存动图时用，pip install celluloid\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import math\n",
    "%matplotlib qt5\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 一阶贝塞尔曲线\n",
    "\n",
    "\n",
    "![在这里插入图片描述](https://img-blog.csdnimg.cn/903d696959fd4e3991e8fecfb40c3a4a.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "MovieWriter ffmpeg unavailable; using Pillow instead.\n"
     ]
    }
   ],
   "source": [
    "P0=np.array([0,0])\n",
    "P1=np.array([1,1])\n",
    "fig=plt.figure(1)\n",
    "camera = Camera(fig)\n",
    "x =[]\n",
    "y=[]\n",
    "for t in np.arange(0,1,0.01):\n",
    "    plt.plot([P0[0],P1[0]],[P0[1],P1[1]],'r')\n",
    "    p1_t=(1-t)*P0+t*P1\n",
    "    x.append(p1_t[0])\n",
    "    y.append(p1_t[1])\n",
    "    # plt.plot(x,y,c='b')\n",
    "    plt.scatter(x,y,c='b')\n",
    "    # plt.pause(0.001)\n",
    "    camera.snap()\n",
    "animation = camera.animate()\n",
    "animation.save('一阶贝塞尔.gif')\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 二阶贝塞尔曲线\n",
    "\n",
    "![在这里插入图片描述](https://img-blog.csdnimg.cn/d6cb97e77e45489499362fba8cfe622c.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "P0 = np.array([0, 0])\n",
    "P1 = np.array([1,1])\n",
    "P2 = np.array([2, 1])\n",
    "fig = plt.figure(2)\n",
    "camera = Camera(fig)\n",
    "\n",
    "x_2 = []\n",
    "y_2 = []\n",
    "for t in np.arange(0, 1, 0.01):\n",
    "    plt.cla()\n",
    "    plt.plot([P0[0], P1[0]], [P0[1], P1[1]], 'k')\n",
    "    plt.plot([P1[0], P2[0]], [P1[1], P2[1]], 'k')\n",
    "    p11_t = (1-t)*P0+t*P1\n",
    "    p12_t = (1-t)*P1+t*P2\n",
    "    p2_t = (1-t)*p11_t+t*p12_t\n",
    "    \n",
    "    x_2.append(p2_t[0])\n",
    "    y_2.append(p2_t[1])\n",
    "    plt.scatter(x_2, y_2, c='r')\n",
    "    plt.plot([p11_t[0],p12_t[0]],[p11_t[1],p12_t[1]],'g')\n",
    "    plt.title(\"t=\"+str(t))\n",
    "    plt.pause(0.001)\n",
    "#     camera.snap()\n",
    "# animation = camera.animate()\n",
    "# animation.save('2阶贝塞尔.gif')\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 三阶贝塞尔曲线\n",
    "\n",
    "![在这里插入图片描述](https://img-blog.csdnimg.cn/c87a2de454814ad9a2adb45959a54886.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "P0 = np.array([0, 0])\n",
    "P1 = np.array([1, 1])\n",
    "P2 = np.array([2, 1])\n",
    "P3 = np.array([3, 0])\n",
    "fig = plt.figure(3)\n",
    "camera = Camera(fig)\n",
    "\n",
    "x_2 = []\n",
    "y_2 = []\n",
    "for t in np.arange(0, 1, 0.01):\n",
    "    plt.cla()\n",
    "    plt.plot([P0[0], P1[0]], [P0[1], P1[1]], 'k')\n",
    "    plt.plot([P1[0], P2[0]], [P1[1], P2[1]], 'k')\n",
    "    plt.plot([P2[0], P3[0]], [P2[1], P3[1]], 'k')\n",
    "    p11_t = (1-t)*P0+t*P1\n",
    "    p12_t = (1-t)*P1+t*P2\n",
    "    p13_t = (1-t)*P2+t*P3\n",
    "    p21_t = (1-t)*p11_t+t*p12_t\n",
    "    p22_t = (1-t)*p12_t+t*p13_t\n",
    "    p3_t = (1-t)*p21_t+t*p22_t\n",
    "\n",
    "    x_2.append(p3_t[0])\n",
    "    y_2.append(p3_t[1])\n",
    "    plt.scatter(x_2, y_2, c='r')\n",
    "\n",
    "    plt.plot([p11_t[0], p12_t[0]], [p11_t[1], p12_t[1]], 'b')\n",
    "    plt.plot([p12_t[0], p13_t[0]], [p12_t[1], p13_t[1]], 'b')\n",
    "\n",
    "    plt.plot([p21_t[0], p22_t[0]], [p21_t[1], p22_t[1]], 'r')\n",
    "    plt.title(\"t=\"+str(t))\n",
    "    plt.pause(0.001)\n",
    "#     camera.snap()\n",
    "# animation = camera.animate()\n",
    "# animation.save('3阶贝塞尔.gif')\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "###  n阶贝塞尔曲线\n",
    "\n",
    "![在这里插入图片描述](https://img-blog.csdnimg.cn/a6030f10dd0744b3a76d1507e1a75f64.png)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 普通方式求解贝塞尔点\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "def bezier_normal(Ps, n, t):\n",
    "    \"\"\"普通方式实现贝塞尔曲线\n",
    "\n",
    "    Args:\n",
    "        Ps (_type_): 控制点，格式为numpy数组：array([[x1,y1],[x2,y2],...,[xn,yn]])\n",
    "        n (_type_): n个控制点，即Ps的第一维度\n",
    "        t (_type_): 时刻t\n",
    "\n",
    "    Returns:\n",
    "        _type_: 当前t时刻的贝塞尔点\n",
    "    \"\"\"\n",
    "    if n==1:\n",
    "        return Ps[0]\n",
    "    p_t = np.array([0,0])\n",
    "    n = len(Ps)-1\n",
    "    for i in range(n+1):\n",
    "        C_n_i = math.factorial(n)/(math.factorial(i)*math.factorial(n-i))\n",
    "        p_t =p_t+C_n_i*(1-t)**(n-i)*t**i*Ps[i]\n",
    "    return p_t\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 递归的方式求解贝塞尔点\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "## 递归的方式求解贝塞尔点\n",
    "def bezier(Ps,n,t):\n",
    "    \"\"\"递归的方式实现贝塞尔曲线\n",
    "\n",
    "    Args:\n",
    "        Ps (_type_): 控制点，格式为numpy数组：array([[x1,y1],[x2,y2],...,[xn,yn]])\n",
    "        n (_type_): n个控制点，即Ps的第一维度\n",
    "        t (_type_): 步长t\n",
    "\n",
    "    Returns:\n",
    "        _type_: 当前t时刻的贝塞尔点\n",
    "    \"\"\"\n",
    "    if n==1:\n",
    "        return Ps[0]\n",
    "    return (1-t)*bezier(Ps[0:n-1],n-1,t)+t*bezier(Ps[1:n],n-1,t)\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "Ps = np.array([[0,0],[1,1],[2,1],[3,0],[4,2]])\n",
    "x_=[]\n",
    "y_=[]\n",
    "for t in np.arange(0,1,0.01):\n",
    "    plt.cla()\n",
    "    # pos = bezier(Ps,len(Ps),t)\n",
    "    pos = bezier_normal(Ps,len(Ps),t)\n",
    "    x_.append(pos[0])\n",
    "    y_.append(pos[1])\n",
    "    plt.plot(Ps[:,0],Ps[:,1])\n",
    "    plt.scatter(x_,y_,c='r')\n",
    "    # print(pos)\n",
    "    # plt.plot(pos[0],pos[1])\n",
    "    plt.pause(0.001)"
   ]
  }
 ],
 "metadata": {
  "interpreter": {
   "hash": "0c7484b3574347463e16b31029466871583b0d4e5c4ad861e8848f2d3746b4de"
  },
  "kernelspec": {
   "display_name": "Python 3.8.12 ('gobigger')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.12"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
